import pygame
import random
import math

from CTextureManager import CTextureManager
from CSoundManager import CSoundManager
from CHUD import CHUD

class CEnemy(object):
    """ Holds enemy data and sprite information """
    
    LEFT = 0
    RIGHT = 1
    UP = 2
    DOWN = 3
    
    def __init__(self , name , startingPoint , map , id ):
        """ Create the player object, binding the sprite sheet image and setting the starting x , y coordinates and a pointer to the map it will be residing in. ID is a unique identifier. """
        
        # Load our spritesheet. SpriteSheets are based directly on the name of the enemy.
        # If our texture isn't loaded, load it, else just get the instance
        if( CTextureManager.IsLoadedTexture(name) == False ):
            self.mSpriteSheet = CTextureManager.LoadTexture("img/E_" + name + ".gif" , name )
        else:
            self.mSpriteSheet = CTextureManager.GetTexture( name )
            
        # Load our audio files. Also based on enemy name.
        # Load attack audio
        if( CSoundManager.IsLoadedSound(name + "_attack") == False ):
            self.mAttackSound = CSoundManager.LoadSound("audio/E_" + name + "_attack.ogg" , name + "_attack" )
        else:
            self.mAttackSound = CSoundManager.GetSound( name + "_attack" )
            
        # Load death sound
        if( CSoundManager.IsLoadedSound(name + "_death") == False ):
            self.mDeathSound = CSoundManager.LoadSound("audio/E_" + name + "_death.ogg" , name + "_death" )
        else:
            self.mDeathSound = CSoundManager.GetSound( name + "_death" )
        
    
        self.mSpriteRect = pygame.Rect( ( 0 , 0 ) , ( 16 , 16 ) )
        self.mXPos = startingPoint[0] * 16
        self.mYPos = startingPoint[1] * 16
        self.mName = name
        
        # Attacking Vars
        self.mIsAttacking = False
        self.mStartedAttacking = 0
        self.mAttackDamage = 1
        
        # Hit Reaction
        self.mIsBeingHit = False
        self.mStartedBeingHit = 0
        
        self.mFollowRadius = 5
        self.mCurrentMap = map
        self.mID = id
        
        # Health and Death
        self.mHealth = 5
        self.mIsAlive = True
        self.mStepsSinceDeath = 0
        
        # Todo: We need a sound manager as well, but instead we will use constants and load if they haven't been loaded.
        
        # Load characteristics for flametail enemy
        if( name == "FlameTail"):
            self.mHealth = 2    
            self.mAttackDamage = 1
            
        if( name == "Slime"):
            self.mHealth = 3
            self.mAttackDamage = 2
            
        if( name == "Ghost"):
            self.mHealth = 8
            self.mAttackDamage = 3
            
    def move(self , direction ):
        """ Move our enemy in a given direction. ( UP , DOWN , LEFT , RIGHT ) """
        
        if( direction == self.LEFT ):
            # Check to see if the block is clear before moving
            if( self.mCurrentMap.isSolidSpace( ( self.GetWorldPos()[0]-1 , self.GetWorldPos()[1] )  ) == False ) :
                self.mXPos -= 16
        elif( direction == self.RIGHT ):
            if( self.mCurrentMap.isSolidSpace( ( self.GetWorldPos()[0]+1 , self.GetWorldPos()[1] )  ) == False ) :
                self.mXPos += 16
        elif( direction == self.UP ):
            if( self.mCurrentMap.isSolidSpace( ( self.GetWorldPos()[0] , self.GetWorldPos()[1]-1 )  ) == False ) :
                self.mYPos -= 16
        elif( direction == self.DOWN ):
            if( self.mCurrentMap.isSolidSpace( ( self.GetWorldPos()[0] , self.GetWorldPos()[1]+1 )  ) == False ) :
                self.mYPos += 16
        else:
            return
        
    def draw(self , inScreen):
        """ Draw our enemy sprite. """
        
        # --- Check our animation timing ---
        # If enemy is attacking and has been for more than 500ms, stop
        if( self.mIsAttacking == True and (pygame.time.get_ticks() - self.mStartedAttacking) >= 500 ):
            self.mIsAttacking = False
            self.mStartedAttacking = 0
            
        # If enemy is being hit and has been for more than 250ms, stop
        if( self.mIsBeingHit == True and (pygame.time.get_ticks() - self.mStartedBeingHit) >= 250 ):
            self.mIsBeingHit = False
            self.mStartedBeingHit = 0
        
        # Determine what frame to play
        if( self.mIsAlive ):
            # Attacking Sprite - only shows if they are not being hit also
            if( self.mIsAttacking == True and self.mIsBeingHit == False):
                self.mSpriteRect.left = 16
            # Being Hit Sprite
            elif( self.mIsBeingHit == True ):
                self.mSpriteRect.left = 32
            # Normal Sprite
            else:
                self.mSpriteRect.left = 0
        # We're dead
        else:
            self.mSpriteRect.left = 48
            
        
        # Blit our sprite
        inScreen.blit( self.mSpriteSheet , ( self.mXPos , self.mYPos ) , self.mSpriteRect )
             
    def Attack(self):
        """ Attack player, play attack sound an animation. """
        self.mAttackSound.play()
        
        self.mStartedAttacking = pygame.time.get_ticks()
        self.mIsAttacking = True
        self.mCurrentMap.GetPlayer().TakeDamage(self.mAttackDamage)
        CHUD.LogMessage("You take " + str(self.mAttackDamage) + " damage from " + self.mName + "!")
        
    def Step(self):
        """ Give this enemy a turn. """
        
        # If our enemy is dead after a step, decompose/remove it
        if( self.mIsAlive == False ):
            
            # If we've been dead for a step remove body, else count up
            # Bug: This does allow the enemy to block the player whlie it is decomposing.
            if( self.mStepsSinceDeath > 0 ):
                self.mCurrentMap.GetEnemies().remove( self )
            else:
                self.mStepsSinceDeath += 1

            return
        
        # Get our player distance once
        playerdist = self.GetPlayerDistance()
        
        # If we are close to player, attack, else roam.
        if( playerdist <= 1):
            self.Attack()
        elif( playerdist > 1 and playerdist < self.mFollowRadius ):
            self.Follow()
        else:
            self.Roam()

    def GetWorldPos(self):
        """ Get the world position tuple of the enemy. Useful for handling collisions within the tile set etc. """
        return ( self.mXPos/16 , self.mYPos/16 )
    
    def Roam(self):
        """ Move the enemy randomly in one direction for one step. """
        self.move( random.randint( 0,3 ) )
        
    def Follow(self):
        """ Move towards the player. """
        
        # Here we use a very straightforward following code
        p = self.mCurrentMap.GetPlayer().GetWorldPos()
        e = self.GetWorldPos()
        
        # Handle X Movement
        if( e[0] > p[0] ):
            self.move( self.LEFT )
        elif( e[0] < p[0] ):
            self.move( self.RIGHT )
            
        # Handle Y Movement
        if( e[1] > p[1] ):
            self.move( self.UP )
        elif( e[1] < p[1] ):
            self.move( self.DOWN )                     

    def Die(self):
        """ Set enemy as dead and do actions required. """
        
        # Bug: Sound plays again when you stab dead enemy, but it's satisfying so we'll leave it.
        self.mDeathSound.play()
        self.mIsAlive = False
        
        # We have won the game
        if( self.mName == "Ghost"):
             self.mCurrentMap.GetPlayer().mDefeatedBoss = True
        
    def TakeDamage( self , damage ):       
        """ Hit the enemy for a specified amount of damage. """   
        self.mHealth -= damage
        
        CHUD.LogMessage("You hit " + self.mName + " for " + str(damage) + " damage! ")

        
        # If we get to zero health or below, die, else hit animation/sound
        if( self.mHealth < 1 ):
            self.Die()
        else:
            self.mIsBeingHit = True
            self.mStartedBeingHit = pygame.time.get_ticks()
        
    def GetName(self):
        """ Get the name of this enemy. """
        return self.mName
    
    def GetPlayerDistance(self):
        """ Get the distance to the player """
        p = self.mCurrentMap.GetPlayer().GetWorldPos()
        e = self.GetWorldPos()
        dx = e[0] - p[0]
        dy = e[1] - p[1]
        return int( math.sqrt(dx**2 + dy**2) )
                
        
        
    
        
        
        
         

